﻿using System;
using System.IO;
using Mono.Cecil.Cil;
using Mono.Cecil.Metadata;

namespace Mono.Cecil.PE
{
	// Token: 0x020000F1 RID: 241
	internal sealed class ImageReader : BinaryStreamReader
	{
		// Token: 0x060009B5 RID: 2485 RVA: 0x00005833 File Offset: 0x00003A33
		public ImageReader(Disposable<Stream> stream, string file_name) : base(stream.value)
		{
			this.image = new Image();
			this.image.Stream = stream;
			this.image.FileName = file_name;
		}

		// Token: 0x060009B6 RID: 2486 RVA: 0x00005866 File Offset: 0x00003A66
		private void MoveTo(DataDirectory directory)
		{
			this.BaseStream.Position = (long)((ulong)this.image.ResolveVirtualAddress(directory.VirtualAddress));
		}

		// Token: 0x060009B7 RID: 2487 RVA: 0x0002B0E0 File Offset: 0x000292E0
		private void ReadImage()
		{
			bool flag = this.BaseStream.Length < 128L;
			if (flag)
			{
				throw new BadImageFormatException();
			}
			bool flag2 = this.ReadUInt16() != 23117;
			if (flag2)
			{
				throw new BadImageFormatException();
			}
			base.Advance(58);
			base.MoveTo(this.ReadUInt32());
			bool flag3 = this.ReadUInt32() != 17744U;
			if (flag3)
			{
				throw new BadImageFormatException();
			}
			this.image.Architecture = this.ReadArchitecture();
			ushort count = this.ReadUInt16();
			this.image.Timestamp = this.ReadUInt32();
			base.Advance(10);
			ushort characteristics = this.ReadUInt16();
			ushort subsystem;
			ushort dllCharacteristics;
			this.ReadOptionalHeaders(out subsystem, out dllCharacteristics);
			this.ReadSections(count);
			this.ReadCLIHeader();
			this.ReadMetadata();
			this.ReadDebugHeader();
			this.image.Characteristics = (uint)characteristics;
			this.image.Kind = ImageReader.GetModuleKind(characteristics, subsystem);
			this.image.DllCharacteristics = (ModuleCharacteristics)dllCharacteristics;
		}

		// Token: 0x060009B8 RID: 2488 RVA: 0x0002B1E8 File Offset: 0x000293E8
		private TargetArchitecture ReadArchitecture()
		{
			return (TargetArchitecture)this.ReadUInt16();
		}

		// Token: 0x060009B9 RID: 2489 RVA: 0x0002B200 File Offset: 0x00029400
		private static ModuleKind GetModuleKind(ushort characteristics, ushort subsystem)
		{
			bool flag = (characteristics & 8192) > 0;
			ModuleKind result;
			if (flag)
			{
				result = ModuleKind.Dll;
			}
			else
			{
				bool flag2 = subsystem == 2 || subsystem == 9;
				if (flag2)
				{
					result = ModuleKind.Windows;
				}
				else
				{
					result = ModuleKind.Console;
				}
			}
			return result;
		}

		// Token: 0x060009BA RID: 2490 RVA: 0x0002B23C File Offset: 0x0002943C
		private void ReadOptionalHeaders(out ushort subsystem, out ushort dll_characteristics)
		{
			bool flag = this.ReadUInt16() == 523;
			this.image.LinkerVersion = this.ReadUInt16();
			base.Advance(44);
			this.image.SubSystemMajor = this.ReadUInt16();
			this.image.SubSystemMinor = this.ReadUInt16();
			base.Advance(16);
			subsystem = this.ReadUInt16();
			dll_characteristics = this.ReadUInt16();
			base.Advance(flag ? 56 : 40);
			this.image.Win32Resources = base.ReadDataDirectory();
			base.Advance(24);
			this.image.Debug = base.ReadDataDirectory();
			base.Advance(56);
			this.cli = base.ReadDataDirectory();
			bool isZero = this.cli.IsZero;
			if (isZero)
			{
				throw new BadImageFormatException();
			}
			base.Advance(8);
		}

		// Token: 0x060009BB RID: 2491 RVA: 0x0002B31C File Offset: 0x0002951C
		private string ReadAlignedString(int length)
		{
			int i = 0;
			char[] array = new char[length];
			while (i < length)
			{
				byte b = this.ReadByte();
				bool flag = b == 0;
				if (flag)
				{
					break;
				}
				array[i++] = (char)b;
			}
			base.Advance(-1 + (i + 4 & -4) - i);
			return new string(array, 0, i);
		}

		// Token: 0x060009BC RID: 2492 RVA: 0x0002B37C File Offset: 0x0002957C
		private string ReadZeroTerminatedString(int length)
		{
			int i = 0;
			char[] array = new char[length];
			byte[] array2 = this.ReadBytes(length);
			while (i < length)
			{
				byte b = array2[i];
				bool flag = b == 0;
				if (flag)
				{
					break;
				}
				array[i++] = (char)b;
			}
			return new string(array, 0, i);
		}

		// Token: 0x060009BD RID: 2493 RVA: 0x0002B3D0 File Offset: 0x000295D0
		private void ReadSections(ushort count)
		{
			Section[] array = new Section[(int)count];
			for (int i = 0; i < (int)count; i++)
			{
				Section section = new Section();
				section.Name = this.ReadZeroTerminatedString(8);
				base.Advance(4);
				section.VirtualAddress = this.ReadUInt32();
				section.SizeOfRawData = this.ReadUInt32();
				section.PointerToRawData = this.ReadUInt32();
				base.Advance(16);
				array[i] = section;
			}
			this.image.Sections = array;
		}

		// Token: 0x060009BE RID: 2494 RVA: 0x0002B450 File Offset: 0x00029650
		private void ReadCLIHeader()
		{
			this.MoveTo(this.cli);
			base.Advance(8);
			this.metadata = base.ReadDataDirectory();
			this.image.Attributes = (ModuleAttributes)this.ReadUInt32();
			this.image.EntryPointToken = this.ReadUInt32();
			this.image.Resources = base.ReadDataDirectory();
			this.image.StrongName = base.ReadDataDirectory();
		}

		// Token: 0x060009BF RID: 2495 RVA: 0x0002B4C4 File Offset: 0x000296C4
		private void ReadMetadata()
		{
			this.MoveTo(this.metadata);
			bool flag = this.ReadUInt32() != 1112167234U;
			if (flag)
			{
				throw new BadImageFormatException();
			}
			base.Advance(8);
			this.image.RuntimeVersion = this.ReadZeroTerminatedString(this.ReadInt32());
			base.Advance(2);
			ushort num = this.ReadUInt16();
			Section sectionAtVirtualAddress = this.image.GetSectionAtVirtualAddress(this.metadata.VirtualAddress);
			bool flag2 = sectionAtVirtualAddress == null;
			if (flag2)
			{
				throw new BadImageFormatException();
			}
			this.image.MetadataSection = sectionAtVirtualAddress;
			for (int i = 0; i < (int)num; i++)
			{
				this.ReadMetadataStream(sectionAtVirtualAddress);
			}
			bool flag3 = this.image.PdbHeap != null;
			if (flag3)
			{
				this.ReadPdbHeap();
			}
			bool flag4 = this.image.TableHeap != null;
			if (flag4)
			{
				this.ReadTableHeap();
			}
		}

		// Token: 0x060009C0 RID: 2496 RVA: 0x0002B5AC File Offset: 0x000297AC
		private void ReadDebugHeader()
		{
			bool isZero = this.image.Debug.IsZero;
			if (isZero)
			{
				this.image.DebugHeader = new ImageDebugHeader(Empty<ImageDebugHeaderEntry>.Array);
			}
			else
			{
				this.MoveTo(this.image.Debug);
				ImageDebugHeaderEntry[] array = new ImageDebugHeaderEntry[this.image.Debug.Size / 28U];
				for (int i = 0; i < array.Length; i++)
				{
					ImageDebugDirectory imageDebugDirectory = new ImageDebugDirectory
					{
						Characteristics = this.ReadInt32(),
						TimeDateStamp = this.ReadInt32(),
						MajorVersion = this.ReadInt16(),
						MinorVersion = this.ReadInt16(),
						Type = (ImageDebugType)this.ReadInt32(),
						SizeOfData = this.ReadInt32(),
						AddressOfRawData = this.ReadInt32(),
						PointerToRawData = this.ReadInt32()
					};
					bool flag = imageDebugDirectory.PointerToRawData == 0 || imageDebugDirectory.SizeOfData < 0;
					if (flag)
					{
						array[i] = new ImageDebugHeaderEntry(imageDebugDirectory, Empty<byte>.Array);
					}
					else
					{
						int position = base.Position;
						try
						{
							base.MoveTo((uint)imageDebugDirectory.PointerToRawData);
							byte[] data = this.ReadBytes(imageDebugDirectory.SizeOfData);
							array[i] = new ImageDebugHeaderEntry(imageDebugDirectory, data);
						}
						finally
						{
							base.Position = position;
						}
					}
				}
				this.image.DebugHeader = new ImageDebugHeader(array);
			}
		}

		// Token: 0x060009C1 RID: 2497 RVA: 0x0002B730 File Offset: 0x00029930
		private void ReadMetadataStream(Section section)
		{
			uint offset = this.metadata.VirtualAddress - section.VirtualAddress + this.ReadUInt32();
			uint size = this.ReadUInt32();
			byte[] data = this.ReadHeapData(offset, size);
			string text = this.ReadAlignedString(16);
			string text2 = text;
			string text3 = text2;
			if (text3 != null)
			{
				switch (text3.Length)
				{
				case 2:
				{
					char c = text3[1];
					if (c != '-')
					{
						if (c != '~')
						{
							break;
						}
						if (!(text3 == "#~"))
						{
							break;
						}
					}
					else if (!(text3 == "#-"))
					{
						break;
					}
					this.image.TableHeap = new TableHeap(data);
					this.table_heap_offset = offset;
					break;
				}
				case 3:
					if (text3 == "#US")
					{
						this.image.UserStringHeap = new UserStringHeap(data);
					}
					break;
				case 4:
					if (text3 == "#Pdb")
					{
						this.image.PdbHeap = new PdbHeap(data);
						this.pdb_heap_offset = offset;
					}
					break;
				case 5:
				{
					char c = text3[1];
					if (c != 'B')
					{
						if (c == 'G')
						{
							if (text3 == "#GUID")
							{
								this.image.GuidHeap = new GuidHeap(data);
							}
						}
					}
					else if (text3 == "#Blob")
					{
						this.image.BlobHeap = new BlobHeap(data);
					}
					break;
				}
				case 8:
					if (text3 == "#Strings")
					{
						this.image.StringHeap = new StringHeap(data);
					}
					break;
				}
			}
		}

		// Token: 0x060009C2 RID: 2498 RVA: 0x0002B8F4 File Offset: 0x00029AF4
		private byte[] ReadHeapData(uint offset, uint size)
		{
			long position = this.BaseStream.Position;
			base.MoveTo(offset + this.image.MetadataSection.PointerToRawData);
			byte[] result = this.ReadBytes((int)size);
			this.BaseStream.Position = position;
			return result;
		}

		// Token: 0x060009C3 RID: 2499 RVA: 0x0002B944 File Offset: 0x00029B44
		private void ReadTableHeap()
		{
			TableHeap tableHeap = this.image.TableHeap;
			base.MoveTo(this.table_heap_offset + this.image.MetadataSection.PointerToRawData);
			base.Advance(6);
			byte sizes = this.ReadByte();
			base.Advance(1);
			tableHeap.Valid = this.ReadInt64();
			tableHeap.Sorted = this.ReadInt64();
			bool flag = this.image.PdbHeap != null;
			if (flag)
			{
				for (int i = 0; i < 58; i++)
				{
					bool flag2 = !this.image.PdbHeap.HasTable((Table)i);
					if (!flag2)
					{
						tableHeap.Tables[i].Length = this.image.PdbHeap.TypeSystemTableRows[i];
					}
				}
			}
			for (int j = 0; j < 58; j++)
			{
				bool flag3 = !tableHeap.HasTable((Table)j);
				if (!flag3)
				{
					tableHeap.Tables[j].Length = this.ReadUInt32();
				}
			}
			ImageReader.SetIndexSize(this.image.StringHeap, (uint)sizes, 1);
			ImageReader.SetIndexSize(this.image.GuidHeap, (uint)sizes, 2);
			ImageReader.SetIndexSize(this.image.BlobHeap, (uint)sizes, 4);
			this.ComputeTableInformations();
		}

		// Token: 0x060009C4 RID: 2500 RVA: 0x0002BA9C File Offset: 0x00029C9C
		private static void SetIndexSize(Heap heap, uint sizes, byte flag)
		{
			bool flag2 = heap == null;
			if (!flag2)
			{
				heap.IndexSize = (((sizes & (uint)flag) > 0U) ? 4 : 2);
			}
		}

		// Token: 0x060009C5 RID: 2501 RVA: 0x0002BAC4 File Offset: 0x00029CC4
		private int GetTableIndexSize(Table table)
		{
			return this.image.GetTableIndexSize(table);
		}

		// Token: 0x060009C6 RID: 2502 RVA: 0x0002BAE4 File Offset: 0x00029CE4
		private int GetCodedIndexSize(CodedIndex index)
		{
			return this.image.GetCodedIndexSize(index);
		}

		// Token: 0x060009C7 RID: 2503 RVA: 0x0002BB04 File Offset: 0x00029D04
		private void ComputeTableInformations()
		{
			uint num = (uint)this.BaseStream.Position - this.table_heap_offset - this.image.MetadataSection.PointerToRawData;
			int num2 = (this.image.StringHeap != null) ? this.image.StringHeap.IndexSize : 2;
			int num3 = (this.image.GuidHeap != null) ? this.image.GuidHeap.IndexSize : 2;
			int num4 = (this.image.BlobHeap != null) ? this.image.BlobHeap.IndexSize : 2;
			TableHeap tableHeap = this.image.TableHeap;
			TableInformation[] tables = tableHeap.Tables;
			for (int i = 0; i < 58; i++)
			{
				Table table = (Table)i;
				bool flag = !tableHeap.HasTable(table);
				if (!flag)
				{
					int num5;
					switch (table)
					{
					case Table.Module:
						num5 = 2 + num2 + num3 * 3;
						break;
					case Table.TypeRef:
						num5 = this.GetCodedIndexSize(CodedIndex.ResolutionScope) + num2 * 2;
						break;
					case Table.TypeDef:
						num5 = 4 + num2 * 2 + this.GetCodedIndexSize(CodedIndex.TypeDefOrRef) + this.GetTableIndexSize(Table.Field) + this.GetTableIndexSize(Table.Method);
						break;
					case Table.FieldPtr:
						num5 = this.GetTableIndexSize(Table.Field);
						break;
					case Table.Field:
						num5 = 2 + num2 + num4;
						break;
					case Table.MethodPtr:
						num5 = this.GetTableIndexSize(Table.Method);
						break;
					case Table.Method:
						num5 = 8 + num2 + num4 + this.GetTableIndexSize(Table.Param);
						break;
					case Table.ParamPtr:
						num5 = this.GetTableIndexSize(Table.Param);
						break;
					case Table.Param:
						num5 = 4 + num2;
						break;
					case Table.InterfaceImpl:
						num5 = this.GetTableIndexSize(Table.TypeDef) + this.GetCodedIndexSize(CodedIndex.TypeDefOrRef);
						break;
					case Table.MemberRef:
						num5 = this.GetCodedIndexSize(CodedIndex.MemberRefParent) + num2 + num4;
						break;
					case Table.Constant:
						num5 = 2 + this.GetCodedIndexSize(CodedIndex.HasConstant) + num4;
						break;
					case Table.CustomAttribute:
						num5 = this.GetCodedIndexSize(CodedIndex.HasCustomAttribute) + this.GetCodedIndexSize(CodedIndex.CustomAttributeType) + num4;
						break;
					case Table.FieldMarshal:
						num5 = this.GetCodedIndexSize(CodedIndex.HasFieldMarshal) + num4;
						break;
					case Table.DeclSecurity:
						num5 = 2 + this.GetCodedIndexSize(CodedIndex.HasDeclSecurity) + num4;
						break;
					case Table.ClassLayout:
						num5 = 6 + this.GetTableIndexSize(Table.TypeDef);
						break;
					case Table.FieldLayout:
						num5 = 4 + this.GetTableIndexSize(Table.Field);
						break;
					case Table.StandAloneSig:
						num5 = num4;
						break;
					case Table.EventMap:
						num5 = this.GetTableIndexSize(Table.TypeDef) + this.GetTableIndexSize(Table.Event);
						break;
					case Table.EventPtr:
						num5 = this.GetTableIndexSize(Table.Event);
						break;
					case Table.Event:
						num5 = 2 + num2 + this.GetCodedIndexSize(CodedIndex.TypeDefOrRef);
						break;
					case Table.PropertyMap:
						num5 = this.GetTableIndexSize(Table.TypeDef) + this.GetTableIndexSize(Table.Property);
						break;
					case Table.PropertyPtr:
						num5 = this.GetTableIndexSize(Table.Property);
						break;
					case Table.Property:
						num5 = 2 + num2 + num4;
						break;
					case Table.MethodSemantics:
						num5 = 2 + this.GetTableIndexSize(Table.Method) + this.GetCodedIndexSize(CodedIndex.HasSemantics);
						break;
					case Table.MethodImpl:
						num5 = this.GetTableIndexSize(Table.TypeDef) + this.GetCodedIndexSize(CodedIndex.MethodDefOrRef) + this.GetCodedIndexSize(CodedIndex.MethodDefOrRef);
						break;
					case Table.ModuleRef:
						num5 = num2;
						break;
					case Table.TypeSpec:
						num5 = num4;
						break;
					case Table.ImplMap:
						num5 = 2 + this.GetCodedIndexSize(CodedIndex.MemberForwarded) + num2 + this.GetTableIndexSize(Table.ModuleRef);
						break;
					case Table.FieldRVA:
						num5 = 4 + this.GetTableIndexSize(Table.Field);
						break;
					case Table.EncLog:
						num5 = 8;
						break;
					case Table.EncMap:
						num5 = 4;
						break;
					case Table.Assembly:
						num5 = 16 + num4 + num2 * 2;
						break;
					case Table.AssemblyProcessor:
						num5 = 4;
						break;
					case Table.AssemblyOS:
						num5 = 12;
						break;
					case Table.AssemblyRef:
						num5 = 12 + num4 * 2 + num2 * 2;
						break;
					case Table.AssemblyRefProcessor:
						num5 = 4 + this.GetTableIndexSize(Table.AssemblyRef);
						break;
					case Table.AssemblyRefOS:
						num5 = 12 + this.GetTableIndexSize(Table.AssemblyRef);
						break;
					case Table.File:
						num5 = 4 + num2 + num4;
						break;
					case Table.ExportedType:
						num5 = 8 + num2 * 2 + this.GetCodedIndexSize(CodedIndex.Implementation);
						break;
					case Table.ManifestResource:
						num5 = 8 + num2 + this.GetCodedIndexSize(CodedIndex.Implementation);
						break;
					case Table.NestedClass:
						num5 = this.GetTableIndexSize(Table.TypeDef) + this.GetTableIndexSize(Table.TypeDef);
						break;
					case Table.GenericParam:
						num5 = 4 + this.GetCodedIndexSize(CodedIndex.TypeOrMethodDef) + num2;
						break;
					case Table.MethodSpec:
						num5 = this.GetCodedIndexSize(CodedIndex.MethodDefOrRef) + num4;
						break;
					case Table.GenericParamConstraint:
						num5 = this.GetTableIndexSize(Table.GenericParam) + this.GetCodedIndexSize(CodedIndex.TypeDefOrRef);
						break;
					case (Table)45:
					case (Table)46:
					case (Table)47:
						goto IL_531;
					case Table.Document:
						num5 = num4 + num3 + num4 + num3;
						break;
					case Table.MethodDebugInformation:
						num5 = this.GetTableIndexSize(Table.Document) + num4;
						break;
					case Table.LocalScope:
						num5 = this.GetTableIndexSize(Table.Method) + this.GetTableIndexSize(Table.ImportScope) + this.GetTableIndexSize(Table.LocalVariable) + this.GetTableIndexSize(Table.LocalConstant) + 8;
						break;
					case Table.LocalVariable:
						num5 = 4 + num2;
						break;
					case Table.LocalConstant:
						num5 = num2 + num4;
						break;
					case Table.ImportScope:
						num5 = this.GetTableIndexSize(Table.ImportScope) + num4;
						break;
					case Table.StateMachineMethod:
						num5 = this.GetTableIndexSize(Table.Method) + this.GetTableIndexSize(Table.Method);
						break;
					case Table.CustomDebugInformation:
						num5 = this.GetCodedIndexSize(CodedIndex.HasCustomDebugInformation) + num3 + num4;
						break;
					default:
						goto IL_531;
					}
					tables[i].RowSize = (uint)num5;
					tables[i].Offset = num;
					num += (uint)(num5 * (int)tables[i].Length);
					goto IL_56B;
					IL_531:
					throw new NotSupportedException();
				}
				IL_56B:;
			}
		}

		// Token: 0x060009C8 RID: 2504 RVA: 0x0002C094 File Offset: 0x0002A294
		private void ReadPdbHeap()
		{
			PdbHeap pdbHeap = this.image.PdbHeap;
			ByteBuffer byteBuffer = new ByteBuffer(pdbHeap.data);
			pdbHeap.Id = byteBuffer.ReadBytes(20);
			pdbHeap.EntryPoint = byteBuffer.ReadUInt32();
			pdbHeap.TypeSystemTables = byteBuffer.ReadInt64();
			pdbHeap.TypeSystemTableRows = new uint[58];
			for (int i = 0; i < 58; i++)
			{
				Table table = (Table)i;
				bool flag = !pdbHeap.HasTable(table);
				if (!flag)
				{
					pdbHeap.TypeSystemTableRows[i] = byteBuffer.ReadUInt32();
				}
			}
		}

		// Token: 0x060009C9 RID: 2505 RVA: 0x0002C128 File Offset: 0x0002A328
		public static Image ReadImage(Disposable<Stream> stream, string file_name)
		{
			Image result;
			try
			{
				ImageReader imageReader = new ImageReader(stream, file_name);
				imageReader.ReadImage();
				result = imageReader.image;
			}
			catch (EndOfStreamException inner)
			{
				throw new BadImageFormatException(stream.value.GetFileName(), inner);
			}
			return result;
		}

		// Token: 0x060009CA RID: 2506 RVA: 0x0002C174 File Offset: 0x0002A374
		public static Image ReadPortablePdb(Disposable<Stream> stream, string file_name, out uint pdb_heap_offset)
		{
			Image result;
			try
			{
				ImageReader imageReader = new ImageReader(stream, file_name);
				uint num = (uint)stream.value.Length;
				imageReader.image.Sections = new Section[]
				{
					new Section
					{
						PointerToRawData = 0U,
						SizeOfRawData = num,
						VirtualAddress = 0U,
						VirtualSize = num
					}
				};
				imageReader.metadata = new DataDirectory(0U, num);
				imageReader.ReadMetadata();
				pdb_heap_offset = imageReader.pdb_heap_offset;
				result = imageReader.image;
			}
			catch (EndOfStreamException inner)
			{
				throw new BadImageFormatException(stream.value.GetFileName(), inner);
			}
			return result;
		}

		// Token: 0x040003E4 RID: 996
		private readonly Image image;

		// Token: 0x040003E5 RID: 997
		private DataDirectory cli;

		// Token: 0x040003E6 RID: 998
		private DataDirectory metadata;

		// Token: 0x040003E7 RID: 999
		private uint table_heap_offset;

		// Token: 0x040003E8 RID: 1000
		private uint pdb_heap_offset;
	}
}
